Introduction
Récemment dans un projet, nous avons souhaité mettre en place des tests unitaires côté front, afin de sécuriser et optimiser le code de l’application WEB. Notre environnement une application React crée à partir de create-react-app. Il n’est pas concevable d’executer les requetes aux APIs, et donc il est nécessaire de mocker la fonction fetch (ou autre appel aynchrone avec la librairie axios par exemple). Plusieurs solutions s’offrent à nous pour mocker cette fonction fetch, et donc spécifier une valeur de retour. Pour rappel, cette fonction est asynchrone (une promise est retournée)
- Mocker manuellement
window.mock
avec Jest, cependant nous verrons qu’il y a quelques inconvénients; - Utiliser une librairie externe pour faire cela, nous verrons comment avec la librairie Fetch-Mock;
- Utiliser un serveur de mock par exemple avec msw, mais je ne voulais pas rentrer dans cette configuration et donc c’est hors scope pour ce post.
Mocker manuellement window.mock
Avec Jest, on peut assez facilement mocker la fonction fetch
.
Pour ce faire, à chaque fois qu’on lance un test, on a plusieurs options : mocker le retour de la fonction fetch
via mockResolvedValue
, ou carrément mocker son implémentation via mockImplementation
(référence : https://jestjs.io/docs/en/mock-function-api)
Explications :
mockResolvedValue()
On peut mocker le retour de la fonction fetch
(qui est une promise pour rappel) grâce à la fonction Jest mockResolvedValue
. Veuillez également noter que la fonction json(
) est également une promise, et donc pensez à bien indiquer async
!
Cela donne :
window.fetch.mockResolvedValue({
ok: true,
json: async () => (/** JSON à retourner */),
})
mockImplementation()
On a plus de souplesse avec la fonction mockImplementation
qu’avec mockResolvedValue
.
En effet, on va pouvoir définir en fonction de l’URL d’entrée de la fonction fetch
, le mock à utiliser. C’est donc plus puissant que la précédente, qui autorise seulement à renvoyer toujours le meme résultat quelquesoit l’URL.
On initialise le mock à chaque test :
beforeEach(() => window.fetch.mockImplementation(mockFetch))`
avec mockFetch
défini comme suit par exemple :
async function mockFetch(url, config) {
switch (url) {
case '/login':
return {
ok: true,
status: 200,
json: async () => (/* Mock retour pour login */),
}
case '/checkout': {
return {
ok: true,
status: 200,
json: async () => (/* Mock retour pour checkout */),
}
}
}
Ainsi, on va pouvoir en fonction de l’URL passsée en paramètre (ici login ou checkout), définir le mock à utiliser de la fonction fetch
.
Une explication détaillée est présente sur ce superbe article (en anglais) : https://kentcdodds.com/blog/stop-mocking-fetch
fetch-mock
Avertissement! Cet aticle étant ancien, il est préférable de ne pas mocker fetch, et utiliser MSW à la place.
Fetch-Mock est une librairie qui va nous permettre de simplifier le fonctionnement ci-dessus. En effet, on va pouvoir définir le mapping entre URL et valeur de retour de la fonction fetch
de manière plus simple qu’avec mockImplementation
.
- Import de la librairie :
import fetchMock from "fetch-mock"`
- Mock de la fonction
fetch
:
fetchMock.mock("myUrl/login"), {/*retour attendu par fetch(sans la promise*/}`
Remarque : il est possible de debugger la librairie en ajoutant DEBUG=fetch-mock*
en paramètre de Jest, dans le package.json
.
Conclusion
Et voilà, nous avons vu différents moyens de mocker cette fonction fetch
, et tester en isolation notre frontend. Dans notre projet, nous avons utilisé la troisième approche avec fetch-mock, mais msw peut être intéressant aussi, ce sera l’objet d’un autre post!